package se.webbzon.boltzmann.game.sequence;

import se.webbzon.boltzmann.game.event.AbstractGameEvent;
import se.webbzon.boltzmann.game.event.SetGameCameraTargetEvent;
import se.webbzon.boltzmann.game.npc.TalkingDuty;
import se.webbzon.boltzmann.game.object.dialogue.AbstractDialogue;
import se.webbzon.boltzmann.game.object.dialogue.DialogueListener;
import se.webbzon.boltzmann.game.object.player.AbstractPlayerCharacter;
import se.webbzon.boltzmann.game.sequence.builder.AbstractSequenceBinding;
import se.webbzon.boltzmann.npc.AbstractNPC;
import se.webbzon.boltzmann.npc.NPCDuty;
import se.webbzon.oschi01.worldobject.WorldObject;
import se.webbzon.oschi01.worldobject.WorldStaticObject;

public abstract class AbstractDialogueSequenceNode extends SequenceNode {
	
	// The dialogue associated with this sequence node
	private final AbstractDialogue dialogue;
	
	// The key used for temporary bindings
	private final String bindingKey;
	
	// The camera event of this dialogue node
	private final AbstractGameEvent cameraEvent;
	
	// The old duty assigned to the non-player character talker
	private NPCDuty oldDuty;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public AbstractDialogueSequenceNode(Sequence sequence, AbstractDialogue dialogue) {
		super(sequence);
		this.dialogue = dialogue;
		bindingKey = toString();
		final boolean all = sequence.getBinding(AbstractSequenceBinding.PLAYER) == null;
		cameraEvent = new SetGameCameraTargetEvent(sequence, new String[] {bindingKey}, all, false);
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Returns the dialogue associated with this dialogue sequence node. **/
	public AbstractDialogue getDialogue() {
		return dialogue;
	}

	/*============================================================
	Public Methods
	============================================================*/
	
	@Override protected void onBegin(WorldObject binding) {
		if (binding instanceof WorldStaticObject) {
			Sequence sequence = getSequence();
			WorldStaticObject wso = (WorldStaticObject) binding;
			dialogue.display(sequence,wso);
			dialogue.addListener(new DialogueListenerImpl(wso));
			
			present(wso);
			
			sequence.getInput().addListener(dialogue);
			
			sequence.bindTo(bindingKey, binding);
			cameraEvent.begin(sequence.getWorld());
		}
	}

	@Override protected void onEnd(WorldObject binding) {
		if (binding instanceof WorldStaticObject) {
			dialogue.hide();
				
			getSequence().getInput().removeListener(dialogue);
			
			cameraEvent.end();
		}
	}
	
	/*============================================================
	Private Methods
	============================================================*/

	/** Checks if the provided bindings has the ability to
	 * animate talking. If so then start the animation. **/
	private final void present(WorldStaticObject binding) {
		if (dialogue.isSpoken()) {
			if (binding instanceof AbstractNPC) {
				AbstractNPC npc = (AbstractNPC) binding;
				oldDuty = npc.getDuty();
				npc.assignDuty(new TalkingDuty(npc,dialogue));
			} else if (binding instanceof AbstractPlayerCharacter) {
				AbstractPlayerCharacter player = 
						(AbstractPlayerCharacter) binding;
				player.setTalking(true);
			}
		}
	}
	
	/** Checks if the provided bindings has the ability to
	 * animate talking. If so then stop the animation. **/
	private final void silence(WorldStaticObject binding) {
		if (dialogue.isSpoken()) {
			if (binding instanceof AbstractNPC) {
				AbstractNPC npc = (AbstractNPC) binding;
				npc.assignDuty(oldDuty);
				oldDuty = null;
			} else if (binding instanceof AbstractPlayerCharacter) {
				AbstractPlayerCharacter player = 
						(AbstractPlayerCharacter) binding;
				player.setTalking(false);
			}
		}
	}
	
	/*============================================================
	Private Classes
	============================================================*/
	
	private final class DialogueListenerImpl implements DialogueListener {
		
		// The current binding of the parent sequence node
		private final WorldStaticObject binding;

		/*============================================================
		Constructors
		============================================================*/
		
		/** Creates a new dialogue listener implementation used
		 * to silence the instance bound to the parent sequence node. **/
		public DialogueListenerImpl(WorldStaticObject binding) {
			this.binding = binding;
		}
		
		/*============================================================
		Public Methods
		============================================================*/
		
		@Override public void onShowInput(AbstractDialogue dialogue) {
			silence(binding);
			dialogue.removeListener(this);
		}
		
	}
	
}
